#include <stdint.h>
#include <stdlib.h>

#define NIT_SHORT_NAMES
#include <nit/macros.h>
#include <nit/palloc.h>
#include <nit/list.h>
#include <nit/hset.h>
#include <nit/hmap.h>
#include <nit/radix.h>
#include <nit/vec.h>

#define ANO_SHORT_NAMES
#include "ano.h"

/* void */
/* ano_forms_init(Ano_forms *form, ) */

void
tscan_init(Ano_tscan *scan, Nit_radix *radix)
{
	scan->radix = radix;
	scan->not_in_use = scan->in_use = NULL;
	scan->pos = 0;
}

static Ano_rilist *
get_old_rilist_or_new(Ano_tscan *scan)
{
	Ano_rilist *list = scan->not_in_use;

	if (!list)
		return palloc(list);

        scan->not_in_use = LIST_NEXT(list, void);
	return list;
}

static int
start_rilist(Ano_tscan *scan)
{
        Ano_rilist *list = get_old_rilist_or_new(scan);

	pcheck(list, 0);
	radix_iter_set(&list->iter, scan->radix);
	list->start = scan->pos;
	LIST_CONS(list, scan->in_use);
	scan->in_use = list;
	return 1;
}

static Ano_bin *
bin_new(Ano_form *form, size_t start)
{
	Ano_bin *bin = palloc(bin);

	pcheck(bin, NULL);
	bin->forms = form;
	bin->start = start;
	LIST_CONS(form, NULL);
	return bin;
}

static int
sort_forms(Ano_ano *ano, Ano_form *form, Ano_reverb *reverb, size_t start)
{
	Ano_bin *list = ano->end;
	Ano_bin *next = NULL;
	Ano_bin *bin;

	if (!list) {
		bin = ano->end = ano->bins = bin_new(form, start);
		pcheck(bin, 0);
		LIST_CONS(bin, NULL);
		DLIST_RCONS(bin, NULL);
		return 1;
	}

	delayed_preveach (list) {
		if (list->start == start) {
			if (reverb)
				return reverb->rank(list, form,
						    reverb->context);

			LIST_CONS(form, list->forms);
			list->forms = form;
			return 1;
		} else if (list->start < start) {
		        pcheck((bin = bin_new(form, start)), 0);

			if (list == ano->end)
				ano->end = bin;

			if (next)
				DLIST_RCONS(next, bin);

			LIST_CONS(list, bin);
			LIST_CONS(bin, next);
			DLIST_RCONS(bin, list);
			return 1;
		}

		next = list;
	}

	pcheck((bin = bin_new(form, start)), 0);
	DLIST_RCONS(ano->bins, form);
	LIST_CONS(form, ano->bins);
	DLIST_RCONS(form, NULL);
	ano->bins = bin;
	return 1;
}

static inline Ano_stat
itemize(Ano_reverb *reverb, void **val)
{
	return reverb->itemize(val, reverb->context);
}

int
tscan_scan_unit(Ano_tscan *scan, Ano_ano *ano, Ano_reverb *reverb,
		void *dat, size_t size)
{
	Ano_rilist *list;
	Ano_rilist *prev = NULL;
	Ano_form *form;

	if (!vec_push(&ano->vec, dat, size))
		return 0;

	if (!start_rilist(scan))
		return 0;

	list = scan->in_use;
	scan->pos += size;

	delayed_foreach (list) {
		void *val;
		/* If we get a non-zero value, not all of dat is in the trie,
		 * so we can't go on.
		 */
		if (radix_iter_move(&list->iter, dat, size)) {
			if (list == scan->in_use)
				scan->in_use = LIST_NEXT(list, void);

			if (prev)
				LIST_CONS(prev, LIST_NEXT(list, void));

			LIST_CONS(list, scan->not_in_use);
			scan->not_in_use = list;
			continue;
		}

		if ((val = radix_iter_get(&list->iter))) {
			if (reverb) {
				switch (itemize(reverb, &val)) {
				case ANO_ERROR:
					return 0;
				case ANO_FILTER:
					return 1;
				case ANO_OK:
					break;
				}
			}

			pcheck((form = palloc(form)), 0);
			form->end = scan->pos;
			form->val = val;
			sort_forms(ano, form, reverb, list->start);
		}

		prev = list;
	}

	return 1;
}

/* int */
/* tscan_predictions(Ano_tscan *scan, Nit_hmap_iter *iter) */
/* { */
/* 	Ano_rilist *list = scan->in_use; */
/* 	Ano_rilist *prev; */

/* 	if (!list) */
/* 		return 0; */

/* 	foreach (list) */
/* 		prev = list; */

/*         ; */
/* } */

int
ano_init(Ano_ano *ano, size_t size)
{
	ano->end = ano->bins = NULL;
	return vec_init(&ano->vec, size);
}
